import 'package:flutter/rendering.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import '../util/dimens.dart';
import 'ItemToList.dart';

abstract class Item<T> {
  Item(
      {this.crossAxisSpacing = 0,
      this.mainAxisSpacing = 0,
      this.crossCount = 1,
      this.mainPixel,
      this.padding});

  ///以下参数主要为CustomScrollView服务
  final int crossCount;
  final double crossAxisSpacing;
  final double mainAxisSpacing;
  final double mainPixel;
  final EdgeInsets padding;

  final String type = T.toString();

  bool get isSliverHead => false;

  bool get isAppBar => false;

  Widget runItemBuild(BuildContext context, int index, dynamic data) {
    return itemBuild(context, index, data as T);
  }

  Widget itemBuild(BuildContext context, int index, T data);
}

class Dazzle2 {
  listView(List<Item> builds,
      {List dataList,
      OnRefreshCallback onRefresh,
      OnLoadCallback onLoad,
      EdgeInsetsGeometry padding = const EdgeInsets.all(0),
      bool shrinkWrap = false,
      bool bottomBouncing = true,
      bool addRepaintBoundaries = true,
      bool topBouncing = true,
      double cacheExtent,
      ScrollPhysics physics = const NeverScrollableScrollPhysics(),
      ScrollController controller2,
      ScrollController controller}) {
    var itemToList = ItemToList(builds);
    return EasyRefresh(
      scrollController: controller,
      topBouncing: topBouncing,
      bottomBouncing: bottomBouncing,
      header: ClassicalHeader(
        enableInfiniteRefresh: false,
        refreshText: "下拉刷新",
        refreshReadyText: "释放刷新",
        refreshingText: "正在刷新...",
        refreshedText: "刷新成功",
        infoText: "更新于 %T",
        refreshFailedText: "刷新失败",
      ),
      footer: ClassicalFooter(
          showInfo: false,
          loadedText: "加载完成",
          loadingText: "正在加载...",
          loadReadyText: "释放加载",
          loadFailedText: "加载失败",
          loadText: "上拉加载"),
      onLoad: onLoad,
      onRefresh: onRefresh,
      child: ListView.builder(
        shrinkWrap: shrinkWrap,
        padding: padding,
        physics: physics,
        cacheExtent: cacheExtent,
        addRepaintBoundaries: addRepaintBoundaries,
        controller: controller2,
        itemBuilder: (BuildContext context, int index) {
          return itemToList.conver(context, index, dataList[index]);
        },
        itemCount: dataList.length,
      ),
    );
  }

  customScrollView(List<Item> builds,
      {List dataList,
      OnRefreshCallback onRefresh,
      OnLoadCallback onLoad,
      bool bottomBouncing = true,
      bool topBouncing = true,
      double cacheExtent,
      ScrollPhysics physics = const NeverScrollableScrollPhysics(),
      EdgeInsets padding = EdgeInsets.zero,
      double crossAxisSpacing = 0,
      bool shrinkWrap = false,
      double mainAxisSpacing = 0,
      ClassicalHeader header,
      ScrollController controller,
      ScrollController controller2}) {
    Type mType;
    var typeList = List<MapEntry<Type, List<dynamic>>>();
    dataList.forEach((data) {
      if (data.runtimeType != mType) {
        mType = data.runtimeType;
        typeList.add(MapEntry(mType, []));
      }
      typeList.last.value.add(data);
    });
    var slivers = <Widget>[];
    if (padding.top > 0) {
      slivers.add(SliverToBoxAdapter(
        child: Container(
          height: padding.top,
        ),
      ));
    }
    typeList.forEach((MapEntry<Type, List<dynamic>> entry) {
      var item = builds.firstWhere((data) => entry.key.toString() == data.type);
      Widget child;
      if (item.isAppBar)
        child = _createSliverAppBar(item, entry.value.first);
      else if (item.isSliverHead)
        child = _createSliverHead(item, entry.value.first);
      else if (entry.key == MoreData) {
        child = _createMoreData(item, entry.value.first);
      } else if (entry.value.length > 1 && item.crossCount == 1)
        child = _createSliverList(item, entry.value);
      else if (entry.value.length > 0 && item.crossCount > 1)
        child = _createSliverGrid(item, entry.value);
      else if (entry.value.length == 1 && item.crossCount == 1)
        child = _createSliverChild(item, entry.value.first);
      if (item.padding != null) child = _createSliverPadding(item, child);
      slivers.add(child);
    });
    if (padding.bottom > 0) {
      slivers.add(SliverToBoxAdapter(
        child: Container(
          height: padding.bottom,
        ),
      ));
    }
    return Padding(
      padding: padding.left > 0 || padding.right > 0
          ? EdgeInsets.only(left: padding.left, right: padding.right)
          : EdgeInsets.zero,
      child: EasyRefresh(
        scrollController: controller,
        bottomBouncing: bottomBouncing,
        taskIndependence: true,
        topBouncing: topBouncing,
        header: header ??
            ClassicalHeader(
              enableInfiniteRefresh: false,
              refreshText: "下拉刷新",
              refreshReadyText: "释放刷新",
              refreshingText: "正在刷新...",
              refreshedText: "刷新成功",
              infoText: "更新于 %T",
              refreshFailedText: "刷新失败",
            ),
        footer: ClassicalFooter(
            showInfo: false,
            loadedText: "加载完成",
            loadingText: "正在加载...",
            loadReadyText: "释放加载",
            loadFailedText: "加载失败",
            loadText: "上拉加载"),
        onRefresh: onRefresh,
        onLoad: onLoad,
        child: CustomScrollView(
          physics: physics,
          shrinkWrap: shrinkWrap,
          cacheExtent: cacheExtent,
          controller: controller2,
          slivers: slivers,
        ),
      ),
    );
  }

  Widget _createSliverChild(Item item, dynamic data) {
    return SliverToBoxAdapter(
      child: Builder(builder: (context) {
        return Container(
          height: item.mainPixel,
          margin: EdgeInsets.symmetric(
              horizontal: item.crossAxisSpacing,
              vertical: item.mainAxisSpacing),
          child: item.runItemBuild(context, 0, data),
        );
      }),
    );
  }

  Widget _createSliverPadding(Item item, Widget sliver) {
    return SliverPadding(
      padding: item.padding,
      sliver: sliver,
    );
  }

  Widget _createMoreData(Item item, dynamic data) {
    return SliverList(
        delegate: SliverChildBuilderDelegate((context, index) {
      return index == 0
          ? Container(
              height: dip(10),
            )
          : Container(
              height: item.mainPixel,
              margin: EdgeInsets.symmetric(
                  horizontal: item.crossAxisSpacing,
                  vertical: item.mainAxisSpacing),
              child: item.runItemBuild(context, index, data),
            );
    }, childCount: 2));
  }

  Widget _createSliverList(Item item, List<dynamic> list) {
    return SliverList(
        delegate: SliverChildBuilderDelegate((context, index) {
      return Container(
        height: item.mainPixel,
        margin: EdgeInsets.symmetric(
            horizontal: item.crossAxisSpacing, vertical: item.mainAxisSpacing),
        child: item.runItemBuild(context, index, list[index]),
      );
    }, childCount: list.length));
  }

  Widget _createSliverGrid(Item item, List<dynamic> list) {
    return SliverGrid(
      delegate: SliverChildBuilderDelegate((context, index) {
        return item.runItemBuild(context, index, list[index]);
      }, childCount: list.length),
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount2(
          crossAxisCount: item.crossCount,
          crossAxisSpacing: item.crossAxisSpacing,
          mainAxisSpacing: item.mainAxisSpacing,
          mainExtent: item.mainPixel),
    );
  }

  Widget _createSliverAppBar(Item item, dynamic value) {
    return item.runItemBuild(null, 0, value);
  }

  Widget _createSliverHead(Item item, dynamic value) {
    return item.runItemBuild(null, 0, value);
  }
}

class SliverGridDelegateWithFixedCrossAxisCount2 extends SliverGridDelegate {
  /// Creates a delegate that makes grid layouts with a fixed number of tiles in
  /// the cross axis.
  ///
  /// All of the arguments must not be null. The `mainAxisSpacing` and
  /// `crossAxisSpacing` arguments must not be negative. The `crossAxisCount`
  /// and `childAspectRatio` arguments must be greater than zero.
  const SliverGridDelegateWithFixedCrossAxisCount2({
    @required this.crossAxisCount,
    this.mainAxisSpacing = 0.0,
    this.crossAxisSpacing = 0.0,
    this.mainExtent = 1.0,
  })  : assert(crossAxisCount != null && crossAxisCount > 0),
        assert(mainAxisSpacing != null && mainAxisSpacing >= 0),
        assert(crossAxisSpacing != null && crossAxisSpacing >= 0),
        assert(mainExtent != null && mainExtent > 0);

  /// The number of children in the cross axis.
  final int crossAxisCount;

  /// The number of logical pixels between each child along the main axis.
  final double mainAxisSpacing;

  /// The number of logical pixels between each child along the cross axis.
  final double crossAxisSpacing;

  /// The ratio of the cross-axis to the main-axis extent of each child.
  final double mainExtent;

  bool _debugAssertIsValid() {
    assert(crossAxisCount > 0);
    assert(mainAxisSpacing >= 0.0);
    assert(crossAxisSpacing >= 0.0);
    assert(mainExtent > 0.0);
    return true;
  }

  @override
  SliverGridLayout getLayout(SliverConstraints constraints) {
    assert(_debugAssertIsValid());
    final double usableCrossAxisExtent =
        constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1);
    final double childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
    final double childMainAxisExtent = mainExtent;
    return SliverGridRegularTileLayout(
      crossAxisCount: crossAxisCount,
      mainAxisStride: childMainAxisExtent + mainAxisSpacing,
      crossAxisStride: childCrossAxisExtent + crossAxisSpacing,
      childMainAxisExtent: childMainAxisExtent,
      childCrossAxisExtent: childCrossAxisExtent,
      reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection),
    );
  }

  @override
  bool shouldRelayout(SliverGridDelegateWithFixedCrossAxisCount2 oldDelegate) {
    return oldDelegate.crossAxisCount != crossAxisCount ||
        oldDelegate.mainAxisSpacing != mainAxisSpacing ||
        oldDelegate.crossAxisSpacing != crossAxisSpacing ||
        oldDelegate.mainExtent != mainExtent;
  }
}

class Dazzle {
  var map = <String, Item>{};
  var _list = [];
  var _maxCount = 2;

  Widget adapter(
      {int maxCount = 1,
      List<Item> builds,
      List dataList,
      EdgeInsetsGeometry padding = const EdgeInsets.all(0),
      double crossAxisSpacing = 0.0,
      double mainAxisSpacing = 0.0,
      ScrollController controller}) {
    _maxCount = maxCount;
    _list = dataList;
    for (Item item in builds) {
      map[item.type] = item;
    }
    return StaggeredGridView.builder(
        padding: padding,
        controller: controller,
        itemCount: _list.length,
        gridDelegate: SliverStaggeredGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: _maxCount,
            crossAxisSpacing: crossAxisSpacing,
            mainAxisSpacing: mainAxisSpacing,
            staggeredTileCount: _list.length,
            staggeredTileBuilder: (index) {
              var model = _list[index];
              if (model == null) return StaggeredTile.count(1, 1);
              var item = map[model.runtimeType.toString()];
              if (item == null) return StaggeredTile.count(1, 1);
              return StaggeredTile.count(item.crossCount, 1);
            }),
        itemBuilder: (context, index) {
          var model = _list[index];
          if (model == null)
            return Container(
              child: Text("找不到相关控件"),
            );
          var item = map[model.runtimeType.toString()];
          if (item == null)
            return Container(
              child: Text("找不到相关控件"),
            );
          return item.runItemBuild(context, index, model);
        });
  }

  Widget autoLineAdapter(
      {List<Item> builds,
      List dataList,
      EdgeInsetsGeometry padding = const EdgeInsets.all(0),
      double crossAxisSpacing = 0,
      bool shrinkWrap = false,
      double mainAxisSpacing = 0,
      ScrollPhysics physics,
      ScrollController controller}) {
    var count = 1;
    _list = dataList;
    for (Item item in builds) {
      map[item.type] = item;
      count = _getLcm(count, item.crossCount);
    }
    _maxCount = count;

    return StaggeredGridView.countBuilder(
        itemCount: _list.length,
        padding: padding,
        crossAxisCount: _maxCount,
        controller: controller,
        shrinkWrap: shrinkWrap,
        physics: physics,
        mainAxisSpacing: mainAxisSpacing,
        crossAxisSpacing: crossAxisSpacing,
        staggeredTileBuilder: (index) {
          if (_list.isEmpty) return StaggeredTile.fit(1);
          var model = _list[index];
          if (model == null) return StaggeredTile.fit(1);
          var item = map[model.runtimeType.toString()];
          if (item == null) return StaggeredTile.fit(1);
          return item.mainPixel == null
              ? StaggeredTile.fit(_maxCount ~/ item.crossCount)
              : StaggeredTile.extent(
                  _maxCount ~/ item.crossCount, item.mainPixel);
        },
        itemBuilder: (context, index) {
          if (_list.isEmpty) return Container();
          var model = _list[index];
          if (model == null)
            return Container(
              child: Text("找不到相关控件"),
            );
          var item = map[model.runtimeType.toString()];
          if (item == null)
            return Container(
              child: Text("找不到相关控件"),
            );
          return item.runItemBuild(context, index, model);
        });
  }

  int _getGcd(int m, int n) {
    while (n > 0) {
      var temp = m % n;
      m = n;
      n = temp;
    }
    return m;
  }

  //求解数m和n和最小公倍数
  int _getLcm(int m, int n) {
    var gcd = _getGcd(m, n);
    return m * n ~/ gcd;
  }
}

//更多数据
class MoreData {
  const MoreData(this.call);

  final VoidCallback call;
}

class MoreDataWidget extends Item<MoreData> {
  @override
  double get mainPixel => dip(50);

  @override
  Widget itemBuild(BuildContext context, int index, MoreData data) {
    data.call();
    return Container(
        alignment: Alignment.center,
        child: SizedBox.fromSize(
          size: Size(dip(36), dip(36)),
          child: CircularProgressIndicator(),
        ));
  }
}

//没有更多
class NoMoreData {}

class NoMoreWidget extends Item<NoMoreData> {
  @override
  double get mainPixel => dip(50);

  @override
  Widget itemBuild(BuildContext context, int index, NoMoreData data) {
    // TODO: implement itemBuild
    return Container(
      alignment: Alignment.center,
      child: Text(
        "- 已经到底了 -",
        style: TextStyle(fontSize: sp(14), color: Color(0xff909090)),
      ),
    );
  }
}

//空数据
class NoData {
  const NoData({this.img = "images/main/order_normal.png", this.hint = "没有数据"});

  final String img;
  final String hint;
}

class NoDataWidget extends Item<NoData> {
  NoDataWidget({double mainPixel, EdgeInsets padding})
      : super(
            mainPixel: mainPixel ?? dip(500),
            padding: padding ?? EdgeInsets.symmetric(horizontal: 120));

  @override
  Widget itemBuild(BuildContext context, int index, NoData data) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Image.asset(
          data.img,
          width: double.infinity,
        ),
        Divider(
          height: dip(15),
          color: Colors.transparent,
        ),
        Text(data.hint,
            textAlign: TextAlign.center,
            style: TextStyle(
              fontSize: sp(14),
              color: Color(0xff949494),
            ))
      ],
    );
  }
}
